// xclick.cpp : simulate mouse click.
// CopyRight (c) 2008 fake, Inc. All Rights Reserved.
// fake@fake.cn
// 2009-04
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <stdarg.h>
#include <getopt.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/extensions/XTest.h>

#define PROG_NAME "xclick"
#define DEFAULT_BUTTON 1
#define DEFAULT_CLICK_COUNT 1
#define DEFAULT_CLICK_INTERVAL 200000
#define APP_VERSION "1.0"


static void useage( void );
static void err_quit( const char *fmt, ... );
static void do_move( Display *dpy, int x, int y );
static void do_click( Display *dpy, int button );
static void pr_cursor_info( Display *dpy );
static void open_display( void );
static void close_display( void );

Display *dpy = NULL;

int main( int argc, char *argv[] )
{
	int xposition = 0;
	int yposition = 0;
	int button = DEFAULT_BUTTON;
	int click_count = DEFAULT_CLICK_COUNT;
	int click_interval = DEFAULT_CLICK_INTERVAL;

	bool need_positon = true;
	bool get_cursor_info_flag = false;
	bool only_move_flag = false;

	const char *short_opts = "hvgmb:i:c:";
	struct option long_opts[] = {
		{ "help", 0, NULL, 'h' },
		{ "version", 0, NULL, 'v' },
		{ "get", 0, NULL, 'g' },
		{ "move", 0, NULL, 'm' },
		{ "button", 1, NULL, 'b' },
		{ "count", 1, NULL, 'c' },
		{ "interval", 1, NULL, 'i' },
		{ NULL, 0, NULL, 0 }
	};

	opterr = 0;
	int opt, opt_index;
	while( (opt = getopt_long( argc, argv, short_opts, long_opts, &opt_index )) != -1 ) {
		switch( opt ) {
		case '?':
			err_quit( "unknow option : %s", argv[optind - 1] );
			break;
		case 'm':
			only_move_flag = true;
			break;
		case 'b':
			button = atoi( optarg );
			break;
		case 'i':
			click_interval = atoi( optarg );
			break;
		case 'c':
			click_count = atoi( optarg );
			if( click_count < 1 )
				click_count = 1;
			break;
		case 'g':
			get_cursor_info_flag = true;
			need_positon = false;
			break;
		case 'v':
			printf( "%s %s\n", PROG_NAME, APP_VERSION );
			exit( EXIT_SUCCESS );
		case 'h':
		default:
			useage();
			exit( EXIT_SUCCESS );
			break;
		}
	}
	if( need_positon ) {
		if( argc - optind < 1 )
			err_quit( "need x, y coordinates" );
		xposition = atoi( argv[optind] );
		yposition = atoi( argv[optind + 1] );
	}

	open_display();
	atexit( close_display );

	if( get_cursor_info_flag ) {
		pr_cursor_info( dpy );
		exit( EXIT_SUCCESS );
	}

	usleep( 200000 );
	do_move( dpy, xposition, yposition );
	if( only_move_flag )
		exit( EXIT_SUCCESS );

	for( int i = 0; i < click_count; i++ ) {
		do_click( dpy, button );
		usleep( click_interval );
	}

	close_display();
	return 0;
}

static void open_display( void )
{
	if( NULL != dpy )
		return;

	dpy = XOpenDisplay( NULL );
	if( NULL == dpy )
		err_quit( "open display faile!!" );
	return;
}

static void close_display( void )
{
	if( NULL == dpy )
		return;

	XCloseDisplay( dpy );
	dpy = NULL;
	return;
}

static void do_move( Display *dpy, int x, int y )
{
	XTestFakeMotionEvent( dpy, -1, x, y, CurrentTime );
	XFlush( dpy );
}

static void do_click( Display *dpy, int button )
{
	XTestFakeButtonEvent( dpy, button, True, CurrentTime );
	XTestFakeButtonEvent( dpy, button, False, CurrentTime );
	XFlush( dpy );
}

static void pr_cursor_info( Display *dpy )
{
	Window r_w, c_w;
	int root_x, root_y, x, y;
	unsigned int mask;

	if( XQueryPointer( dpy, RootWindow( dpy, DefaultScreen( dpy )), &r_w, &c_w,
					&root_x, &root_y, &x, &y, &mask ) == False ) {
		err_quit( "XQueryPointer return False." );
	}

	XTextProperty name;
	if( XGetWMName( dpy, c_w, &name ) && name.value ) {
		printf( "Window under cursor:\nWindow: 0x%ld\nName:%s\n",
				c_w, name.value );
		XFree((char *)name.value );
	}
	printf( "Cursor position:\nX %d\nY %d\n", root_x, root_y );
	return;
}

static void err_quit( const char *fmt, ... )
{
	va_list ap;

	va_start( ap, fmt );
	vfprintf( stderr, fmt, ap );
	fprintf( stderr, "\n" );
	va_end( ap );

	exit( EXIT_FAILURE );
}

static void useage( void )
{
	printf( "%s -- Version %s\n"
			"CopyRight fake 2009. All Rights Reserved\n"
			"Build : %s\n",
			PROG_NAME, APP_VERSION, __DATE__ );
	printf( "Useage : %s [-g] [-m] [-b button] X Y\n"
			"        -g, --get                get cursor information.\n"
			"        -m, --move               only move.\n"
			"        -b, --button <button>    specify mouse button.\n"
			"                                 1 == left, 2 == middle, 3 == right. default %d.\n"
			"        -i, --interval           sleep time(usec) between two clicks.\n"
			"                                 default %d.\n"
			"        -c, --count              click times.\n"
			"                                 default %d.\n"
			"        X, Y                     x coordinate, y coordinate.\n"
			"        -v, --version            show version.\n"
			"        -h, --help               show this help message.\n",
			PROG_NAME, DEFAULT_BUTTON, DEFAULT_CLICK_INTERVAL, DEFAULT_CLICK_COUNT );
	return;
}
